package fr.ul.ade.prime.ui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragment;

import fr.ul.ade.R;
import fr.ul.ade.prime.config.Config;
import fr.ul.ade.prime.exception.AAAException;
import fr.ul.ade.prime.model.Project;
import fr.ul.ade.prime.model.Resource;
import fr.ul.ade.prime.model.StudentNumber;
import fr.ul.ade.prime.provider.AdeWebContract;
import fr.ul.ade.prime.ui.util.SchedulePeriod;
import fr.ul.ade.prime.ui.util.ScheduleProject;
import fr.ul.ade.prime.ui.util.ScheduleResource;
import fr.ul.ade.prime.util.Lists;

public class CreateScheduleFragment extends SherlockFragment implements
		LoaderManager.LoaderCallbacks<Cursor>, OnClickListener {

	@SuppressWarnings("unused")
	private static final String TAG = CreateScheduleFragment.class
			.getSimpleName();

	private static final long DEFAULT_ID = -1;

	/**
	 * Composant contenant les différents champs ressources du formulaire.
	 */
	private LinearLayout fields;

	/**
	 * L'index d'insertion pour les nouvelles ressources.
	 */
	protected int newResIndex;

	/**
	 * La correspondance de la période entre le texte et les valeurs. Ex : 1
	 * semaine -> 7
	 */
	private List<Integer> correspondance;

	/**
	 * Adaptateur du spinner de projet
	 */
	private ProjectAdapter pAdapter;

	/**
	 * Cursor utilisé pour contenir les informations de l'emploi du temps
	 * affiché en attendnat d'avoir chargé les projets.
	 */
	private Cursor scheduleCursor;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.newResIndex = 0;
		this.initCorrespondance();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View v = inflater.inflate(R.layout.create_schedule, container, false);

		this.fields = (LinearLayout) v.findViewById(R.id.resources_container);
		this.addResource();

		final TextView resourceTitle = (TextView) v
				.findViewById(R.id.addResource);
		resourceTitle.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View view) {
				CreateScheduleFragment.this.addResource();
			}
		});

		final Button saveButton = (Button) v.findViewById(R.id.saveSchedule);
		final Button cancelButton = (Button) v
				.findViewById(R.id.cancelSchedule);
		saveButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View view) {
				CreateScheduleFragment.this.handleSave();
			}
		});
		cancelButton.setOnClickListener(this);
		return v;
	}

	/**
	 * Ajoute une nouvelle ressource à la vue.
	 * 
	 * @param resource
	 *            valeur de la resource ajoutée
	 */
	public void addResource(String resource) {
		LayoutInflater inflater = LayoutInflater.from(this.getActivity());
		ScheduleResource newRes = new ScheduleResource(inflater, resource,
				this.getActivity());
		final View newResView = newRes.getView(null, null);

		this.fields.addView(newResView, this.newResIndex);
		final LinearLayout mFields = this.fields;
		newResView.findViewById(R.id.remove_resource)
				.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						// On ne peut pas supprimer la dernière ressource, il en
						// faut toujours au moins une
						// CreateScheduleFragment.this
						// .removeResource(newResView);
						// if (mFields.getChildCount() == 0) {
						// CreateScheduleFragment.this.addResource();
						// }
						if (mFields.getChildCount() > 1) {
							CreateScheduleFragment.this.removeResource(newResView);
						} else {
							((TextView) mFields.getChildAt(0)
									.findViewById(R.id.refSchedule))
									.setText(""); //$NON-NLS-1$
						}// if
					}
				});
		this.newResIndex++;
	}// addResource(String)

	/**
	 * Supprime les champs ressources laissés vide, afin d'obtenir des champs
	 * remplis de façon contigue.
	 */
	public void removeEmptyResources() {
		for (int i = 0; i < this.fields.getChildCount();) {
			View child = this.fields.getChildAt(i);
			String resource = ((EditText) child.findViewById(R.id.refSchedule))
					.getText().toString();
			if (resource.length() == 0) {
				this.fields.removeViewAt(i);
				this.newResIndex--;
			} else {
				i++;
			}// if
		}// for
	}// removeEmptyResources()

	/**
	 * Ajoute une nouvelle ressource à la vue.
	 */
	public void addResource() {
		this.addResource("");//$NON-NLS-1$
	}// addResource()

	/**
	 * Supprime une ressource de la vue.
	 * 
	 * @param removedView
	 *            view which should well be removed
	 * 
	 * @see ScheduleResource
	 */
	public void removeResource(View removedView) {
		this.fields.removeView(removedView);
		this.newResIndex--;
	}// removeResource()

	/**
	 * Change le projet de la vue.
	 * 
	 * @param projectId
	 *            l'id du projet
	 * 
	 * @see ScheduleProject
	 */
	public void setProject(long projectId) {
		this.pAdapter.setSelection(projectId);
	}// setProject(long)

	/**
	 * Change la période de la vue.
	 * 
	 * @param period
	 *            période de synchronisation
	 * 
	 * @see SchedulePeriod
	 */
	public void setPeriod(int period) {
		View view = this.getView();
		Spinner periodView = (Spinner) view.findViewById(R.id.spinnerPeriod);
		periodView.setSelection(this.getPositionWithPeriod(period));
	}// setPeriod(int)

	/**
	 * Initialise la liste de correspondance Texte -> Période 1 semaine = 7 2
	 * semaines = 14 3 semaines = 21 1 mois = 28 5 mois = 150
	 */
	public void initCorrespondance() {
		this.correspondance = new ArrayList<Integer>();
		@SuppressWarnings("boxing")
		Integer[] values = new Integer[] { 7, 14, 21, 28, 150 };
		this.correspondance.addAll(Arrays.asList(values));
	}// initCorrespondance()

	/**
	 * Récupère la période avec son index dans la liste.
	 * 
	 * @param position
	 *            index de l'item selectionné dans le spinner
	 * @return la période associé à l'item selectionné dans le spinner
	 */
	public Integer getPeriodWithPosition(int position) {
		return this.correspondance.get(position);
	}// getPeriodWithPosition(int)

	/**
	 * Récupère la position pour la période dans la liste.
	 * 
	 * @param période
	 *            période dont on recherche l'indice dans le spinner
	 * @return la postion associé à la période dans le spinner
	 */
	public int getPositionWithPeriod(int period) {
		int position = 0;
		Iterator<Integer> iter = this.correspondance.iterator();
		int minDiff = Math.abs(iter.next().intValue() - period);
		int currentPos = 1;
		while (iter.hasNext() && (minDiff > 0)) {
			int diff = Math.abs(iter.next().intValue() - period);
			if (diff < minDiff) {
				position = currentPos;
				minDiff = diff;
			}// if
			++currentPos;
		}// while
		return position;
	}// getPositionWithPeriod(int)

	/**
	 * Récupère les valeurs des champs et appelle la fonction de traitement de
	 * l'activité associée.
	 */
	protected void handleSave() {
		View view = this.getView();
		// récupéreation du nom de l'edt
		EditText nameView = (EditText) view.findViewById(R.id.nameSchedule);
		String name = nameView.getText().toString();
		// récupéreation de la période
		Spinner periodView = (Spinner) view.findViewById(R.id.spinnerPeriod);
		int period = this
				.getPeriodWithPosition(periodView.getSelectedItemPosition())
				.intValue();
		// récupéreation des projets
		Spinner projectView = (Spinner) view.findViewById(R.id.spinnerProject);
		Project project = this.pAdapter.getProject(projectView
				.getSelectedItemPosition());

		// récupéreation des ressouces
		int nResources = this.fields.getChildCount();
		List<Resource> resources = new ArrayList<Resource>();
		for (int i = 0; i < nResources; i++) {
			String resource = ((EditText) this.fields.getChildAt(i)
					.findViewById(R.id.refSchedule)).getText().toString();
			if (resource.length() != 0)
				resources.add((resource.length() == 8) ? new StudentNumber(
						resource, "") : new Resource(resource));//$NON-NLS-1$
		}
		Resource[] res = resources.toArray(new Resource[resources.size()]);

		try {
			((CreateSchedule) this.getActivity()).handleSave(name,
					res,
					period,
					project);
			this.getActivity().onBackPressed();
		} catch (AAAException e) {
			Toast.makeText(this.getActivity(),
					e.getMessage(),
					Toast.LENGTH_LONG).show();
		}
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		LoaderManager manager = this.getLoaderManager();
		// Cursor pour le chargement des projets
		if (manager.getLoader(ProjectQuery._TOKEN) == null) {
			manager.initLoader(ProjectQuery._TOKEN, null, this);
		} else {
			manager.restartLoader(ProjectQuery._TOKEN, null, this);
		}// if
		Bundle extras = this.getActivity().getIntent().getExtras();
		if (extras != null) {
			long scheduleId = extras
					.getLong(BundleExtraNames.EXTRA_SCHEDULE_ID, DEFAULT_ID);
			if (scheduleId != DEFAULT_ID) {
				if (manager.getLoader(ScheduleQuery._TOKEN) == null) {
					manager.initLoader(ScheduleQuery._TOKEN, null, this);
				} else {
					manager.restartLoader(ScheduleQuery._TOKEN, null, this);
				}// if
			}// if
		}// if
	}

	@Override
	public Loader<Cursor> onCreateLoader(int id, Bundle data) {
		Uri uri = null;
		String[] projection = null;
		String selection = null;
		String[] selectionArgs = null;
		String sortOrder = null;

		if (id == ProjectQuery._TOKEN) {
			uri = AdeWebContract.Project.CONTENT_URI;
			projection = ProjectQuery.PROJECTION;
			sortOrder = AdeWebContract.Project.DEFAULT_SORT;
		} else /* if (id == ScheduleQuery._TOKEN) */{
			long scheduleId = this.getActivity().getIntent().getExtras()
					.getLong(BundleExtraNames.EXTRA_SCHEDULE_ID);
			uri = ContentUris
					.withAppendedId(AdeWebContract.Schedule.CONTENT_URI,
							scheduleId);
			projection = ScheduleQuery.PROJECTION;
			sortOrder = AdeWebContract.Schedule.DEFAULT_SORT;
		}// if

		return new CursorLoader(this.getActivity(), uri, projection, selection,
				selectionArgs, sortOrder);
	}

	@Override
	public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
		if (loader.getId() == ProjectQuery._TOKEN) {
			Spinner spinner = (Spinner) this.getActivity()
					.findViewById(R.id.spinnerProject);
			this.pAdapter = new ProjectAdapter(this.getActivity(), cursor);
			spinner.setAdapter(this.pAdapter);
			this.pAdapter.notifyDataSetChanged();
			if (this.scheduleCursor != null) {
				this.initFields(this.scheduleCursor);
			}// if
		} else /* if(loader.getId() == ScheduleQuery._TOKEN) */{
			if (this.pAdapter == null) {
				this.scheduleCursor = cursor;
			} else {
				this.initFields(cursor);
			}
		}
	}

	@Override
	public void onLoaderReset(Loader<Cursor> loader) {
		/* Nothing to be done. */
	}

	protected void initFields(Cursor cursor) {
		if (cursor.moveToNext()) {
			this.initFields(cursor.getString(ScheduleQuery.SCHEDULE_NAME),
					cursor.getString(ScheduleQuery.SCHEDULE_RESOURCES),
					cursor.getString(ScheduleQuery.SCHEDULE_STUDENT_NUM),
					cursor.getInt(ScheduleQuery.SCHEDULE_PERIOD),
					cursor.getInt(ScheduleQuery.SCHEDULE_PROJECT_ID));
		}// if
	}

	protected void initFields(String name, String resources,
			String studentNumbers, int period, long projectId) {
		// On supprime les ressources existantes
		this.fields.removeAllViews();
		this.newResIndex = 0;

		View view = this.getView();
		// Initialisation du nom de l'edt
		EditText nameView = (EditText) view.findViewById(R.id.nameSchedule);
		nameView.setText(name);
		// Initialisation de la période
		Spinner periodView = (Spinner) view.findViewById(R.id.spinnerPeriod);
		periodView.setSelection(this.getPositionWithPeriod(period));
		// Initialisation des projets
		Spinner projectView = (Spinner) view.findViewById(R.id.spinnerProject);
		projectView.setSelection(this.pAdapter.getProjectPosition(projectId));
		// Initialisation des ressources
		String[] resArray;
		if (!TextUtils.isEmpty(resources)) {
			resArray = resources.split(Config.ESCAPED_RESOURCE_SEPARATOR);
			for (String resource : resArray) {
				this.addResource(resource);
			}
		}
		if (!TextUtils.isEmpty(studentNumbers)) {
			resArray = studentNumbers.split(Config.ESCAPED_RESOURCE_SEPARATOR);
			for (String resource : resArray) {
				this.addResource(resource);
			}
		}
	}

	protected interface ProjectQuery {
		// id pour le loader
		int _TOKEN = 0xffffffff; // -1

		// colonnes demandées au provider
		String[] PROJECTION = { BaseColumns._ID,
				AdeWebContract.Project.PROJECT_NAME,
				AdeWebContract.Project.PROJECT_ADEWEB_ID };

		// colonnes des données demandés pour utiliser lors de la récupération
		// des données depuis le Cursor (appels à getString(int) p.ex.)
		int _ID = 0;
		int PROJECT_NAME = 1;
		int PROJECT_ADEWEB_ID = 2;
	}

	protected interface ScheduleQuery {
		// id pour le loader
		int _TOKEN = 0x2;

		// colonnes demandées au provider
		String[] PROJECTION = { BaseColumns._ID,
				AdeWebContract.Schedule.SCHEDULE_NAME,
				AdeWebContract.Schedule.SCHEDULE_RESOURCES,
				AdeWebContract.Schedule.SCHEDULE_STUDENT_NUM,
				AdeWebContract.Schedule.SCHEDULE_PERIOD,
				AdeWebContract.Schedule.SCHEDULE_PROJECT_ID };

		// colonnes des données demandés pour utiliser lors de la récupération
		// des données depuis le Cursor (appels à getString(int) p.ex.)
		int _ID = 0;
		int SCHEDULE_NAME = 1;
		int SCHEDULE_RESOURCES = 2;
		int SCHEDULE_STUDENT_NUM = 3;
		int SCHEDULE_PERIOD = 4;
		int SCHEDULE_PROJECT_ID = 5;
	}

	/**
	 * Permet à partir d'un cursor(Project) de construire un item projet(string)
	 * pour un spinner.
	 * 
	 * @author Seb
	 */
	private class ProjectAdapter extends ArrayAdapter<String> {
		private List<Long> projectIds;
		private List<Long> adeProjectIds;
		private Integer selection;

		public ProjectAdapter(Activity activity) {
			super(activity, android.R.layout.simple_spinner_item);
			this.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
			this.projectIds = Lists.newArrayList();
			this.adeProjectIds = Lists.newArrayList();
			this.selection = Integer.valueOf(-1);
		}

		public ProjectAdapter(Activity activity, Cursor cursor) {
			this(activity);
			this.addCursor(cursor);
		}

		public void setSelection(long projectId) {
			this.setSelection(this.getProjectPosition(projectId));
		}

		public void setSelection(int id) {
			if (this.projectIds.isEmpty()) {
				View view = CreateScheduleFragment.this.getView();
				Spinner projectView = (Spinner) view
						.findViewById(R.id.spinnerProject);
				projectView.setSelection(id);
			} else {
				synchronized (this.selection) {
					this.selection = Integer.valueOf(id);
				}
			}// if
		}

		public void addCursor(Cursor cursor) {
			while (cursor.moveToNext()) {
				this.add(cursor.getString(ProjectQuery.PROJECT_NAME));
				this.projectIds.add(Long.valueOf(cursor
						.getLong(ProjectQuery._ID)));
				this.adeProjectIds.add(Long.valueOf(cursor
						.getLong(ProjectQuery.PROJECT_ADEWEB_ID)));
			}
			synchronized (this.selection) {
				if (this.selection.intValue() != -1) {
					this.setSelection(this.selection.intValue());
				}
				this.selection = Integer.valueOf(-1);
			}
		}

		public Project getProject(int selectedIndex) {
			return new Project(this.projectIds.get(selectedIndex).intValue(),
					-1, null);
		}

		public int getProjectPositionFromAdeProjectId(long adeProjectId) {
			return this.adeProjectIds.indexOf(Long.valueOf(adeProjectId));
		}

		public int getProjectPosition(long projectId) {
			int pos = this.projectIds.indexOf(Long.valueOf(projectId));
			if (pos == -1) {
				pos = this.getProjectPositionFromAdeProjectId(projectId);
			}
			return pos;
		}
	}

	/**
	 * Annule la création/l'édition et retourne vers l'écran précédant.
	 */
	@Override
	public void onClick(View actualView) {
		this.getActivity().onBackPressed();
	}// onClick(View)

}// class CreateScheduleFragment